Audio Stream
In this tutorial we set up and play a continuous audio waveform using a buffer and event-driven playback mechanism.
Download original notebookThere is only one built-in option to stream raw audio data - PCMPlayer, which accepts a audio buffers in a form of NumericArray
of singed integers (as most audio sources):
abuffer = NumericArray[0.2 RandomInteger[{-32760,32760}, 40 1024], "Integer16", "ClipAndRound"];
PCMPlayer[abuffer, "SignedInteger16"]
This will generate a second of a white noise
For further example we generate audio on-fly using samplingFunction
, which defines a pleasant waveform to be played:
samplingFunction = Function[t, Sin[200.0 t]]
In this case, it’s a sine wave of frequency 200 Hz.
For continuous generation we need to track global time and supply new chunks of data to PCMPlayer. The latter is event-driven and it will fire an event before the previous chunk of data is almost depleted:
buffer = NumericArray[Table[0, {256}], "Integer16", "ClipAndRound"]; samplingFunction = Function[t, Sin[200.0 t]]; time = 0; EventHandler["bufferEnds", {"More" -> Function[Null, With[{c = (1.0/44100.0) (2Pi)}, With[{sampled = 32760 Table[samplingFunction[c (i + time)], {i, 0, 4 1024 - 1}]}, buffer = NumericArray[sampled, "Integer16", "ClipAndRound"]; ]; time += 4 1024; ]; ]}]; PCMPlayer[buffer // Offload, "SignedInteger16", "Event"->"bufferEnds"]
We can change the generator function on-fly
samplingFunction = Function[t, Sin[300.0 t ]];
Two oscillators with a decay
samplingFunction = Function[t, With[{mod = Mod[t, Pi]}, Sin[300.0 t ] Sin[45.0 t ] Exp[-t mod / 200.0 ] ]];
Waveform screen
Let's modify our handler function to store waveform in ilines
symbol
EventHandler["bufferEnds", {"More" -> Function[Null, With[{c = (1.0/44100.0) (2Pi)}, With[{sampled = 32760 Table[samplingFunction[c (i + time)], {i, 0, 4 1024 - 1}]}, buffer = NumericArray[sampled, "Integer16", "ClipAndRound"]; ilines = NumericArray[ Transpose[{Range[4 1024], sampled}] , "Integer16", "ClipAndRound"]; ]; time += 4 1024; ]; ]}];
Project the cell below to a window
Graphics[{Line[ilines // Offload]}, "TransitionType"->None, AspectRatio->0.5, "Controls"->False, PlotRange->{{0, 4 1024}, {-32760, 32760}}]
GUI controls
Here we set up a bunch of sliders to control the oscillator parameters
EventHandler[InputGroup[<| "w1" -> InputRange[10, 800, 1, 455, "Label"->"Osc 1"], "w2" -> InputRange[10, 800, 1, 455, "Label"->"Osc 2"], "d" -> InputRange[50, 1000, 10, 300, "Label"->"Decay"], "r" -> InputRange[0.5, 4.5, 0.1, 3.1, "Label"->"Period"] |>, "Layout"->"Horisontal"], With[{}, samplingFunction = Function[t, With[{mod = Mod[t, #r]}, Sin[#w1 t ] Sin[#w2 t ] Exp[-t mod / #d ] ]]; ]&]
See it in action ✨